enum Instruction {
    Noop,
    AddX(i32),
}

impl From<&str> for Instruction {
    fn from(value: &str) -> Self {
        match &value[..4] {
            "noop" => Instruction::Noop,
            "addx" => Instruction::AddX(value[5..].parse::<i32>().unwrap()),
            _ => panic!("Unspport inst '{}'", &value[..4]),
        }
    }
}

struct CPU {
    reg_x: i32,
    reg_x_values: Vec<i32>,
}

impl CPU {
    fn new() -> Self {
        Self {
            reg_x: 1, 
            reg_x_values: vec![1],
        }
    }

    fn execute(&mut self, inst: Instruction) {
        match inst {
            Instruction::Noop => {
                // Nothing to do
                self.reg_x_values.push(self.reg_x);
            }
            Instruction::AddX(value) => {
                self.reg_x_values.push(self.reg_x);
                self.reg_x += value;
                self.reg_x_values.push(self.reg_x);
            }
        }
    }
}

fn parse_input() -> Vec<Instruction> {
    include_str!("../input.txt")
        .lines()
        .map(Into::<Instruction>::into)
        .collect()
}

fn part1() {
    let insts = parse_input();
    let mut cpu = CPU::new();
    for inst in insts {
        cpu.execute(inst);
    }

    let sum = [20, 60, 100, 140, 180, 220]
        .into_iter()
        .map(|tick| tick * cpu.reg_x_values[tick as usize - 1])
        .sum::<i32>();

    println!("Part 1: {}", sum);
}

fn part2() {
    let insts = parse_input();
    let mut cpu = CPU::new();
    for inst in insts {
        cpu.execute(inst);
    }

    let mut crt = Vec::new();
    for row in 0..6 {
        let mut line = String::new();
        for col in 0..40 {
            let tick = (row * 40 + col) as usize;
            let sprite_pos = cpu.reg_x_values[tick] - 1;
            line.push(
                if col >= sprite_pos && col <= sprite_pos + 2 { '#' } else { '.' }
            );
        }
        crt.push(line);
    }

    println!("Part 2:");
    for line in crt {
        println!("{}", line);
    }
}

fn main() {
    part1();
    part2();
}
